package com.haoran.dwz.common.security;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.UrlPathHelper;

import com.alibaba.fastjson.JSONObject;
import com.haoran.dwz.rest.R;
import com.haoran.dwz.rest.ReturnCode;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
	
	private Logger logger =LoggerFactory.getLogger(getClass());
	
	private final UrlPathHelper pathHelper = new UrlPathHelper();
	
	@Autowired
	private UserDetailsService userDetailsService;

	@Autowired
	private JwtTokenHandler jwtTokenHandler;

	@Value("${jwt.header:Authorization}")
	private String tokenHeader;

	@Value("${jwt.tokenHead:Bearer }")
	private String tokenHead;

	@Autowired
	private ApiPathPattern apiPathPattern;
 
	/**
	 * 以api开始的url都需要鉴权
	 */
	@Override
	protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
 
		String lookupPath = this.pathHelper.getLookupPathForRequest(request);
		///可以不用鉴权的请求
		if(apiPathPattern.getPublicRequestMatcher().matches(request)) {
			logger.debug("访问此路径: "+ lookupPath  +" 不需要需要鉴权");
			return true;
		}else if(apiPathPattern.getSecurityRequestMatcher().matches(request)) {
			logger.debug("访问此路径: "+ lookupPath  +" 需要鉴权");
			return false;
		}else {
			logger.debug("访问此路径: "+ lookupPath  +" 不需要需要鉴权");
			return true;
		}
	}

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws ServletException, IOException {
		String authHeader = request.getHeader(this.tokenHeader);
		if (authHeader != null && authHeader.startsWith(tokenHead)) {
			final String authToken = authHeader.substring(tokenHead.length()); // The part after "Bearer "
			String account = jwtTokenHandler.getUsernameFromToken(authToken);
			logger.info("checking authentication " + account);
			if (account != null && SecurityContextHolder.getContext().getAuthentication() == null) {
				// 如果我们足够相信token中的数据，也就是我们足够相信签名token的secret的机制足够好
				// 这种情况下，我们可以不用再查询数据库，而直接采用token中的数据
				// 本例中，我们还是通过Spring Security的 @UserDetailsService 进行了数据查询
				// 但简单验证的话，你可以采用直接验证token是否合法来避免昂贵的数据查询
				UserDetails userDetails = this.userDetailsService.loadUserByUsername(account);
				if (jwtTokenHandler.validateToken(authToken, userDetails)) {
					UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
							userDetails, null, userDetails.getAuthorities());
					authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
					logger.info("authenticated user " + account + ", setting security context");
					SecurityContextHolder.getContext().setAuthentication(authentication);
					chain.doFilter(request, response);
				} else {
					responseOutWithJson(response, R.result(ReturnCode.TOKEN_ERROR, "请重新登录"));
				}
			}else {
				responseOutWithJson(response, R.result(ReturnCode.TOKEN_ERROR, "请重新登录"));
			}
		}else {
			responseOutWithJson(response, R.result(ReturnCode.AUTH_ERROR, "请求头需携带以Authorization为key, 以'"+ tokenHead + "'为前缀的token参数"));
		}
	}

	protected void responseOutWithJson(HttpServletResponse response, Object responseObject) {
		// 将实体对象转换为JSON Object转换
		String responseJSONObject = JSONObject.toJSONString(responseObject);
		response.setCharacterEncoding("UTF-8");
		response.setContentType("application/json; charset=UTF-8");
		PrintWriter out = null;
		try {
			out = response.getWriter();
			out.append(responseJSONObject);
			logger.debug("返回是\n");
			logger.debug(responseJSONObject);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				out.close();
			}
		}
	}

	public JwtTokenHandler getJwtTokenHandler() {
		return jwtTokenHandler;
	}

	public void setJwtTokenHandler(JwtTokenHandler jwtTokenHandler) {
		this.jwtTokenHandler = jwtTokenHandler;
	}
}
